package server

import (
	"encoding/json"

	"github.com/dailing/levlog"
	"github.com/gomodule/redigo/redis"
)

type Task struct {
	conn           *redis.Pool
	taskName       string
	payloadChannel string
	outputSlot     string
	waitSlot       string
}

type TaskRequest struct {
	payload []byte
}

type TaskResponseWriter struct {
	slot string
	conn *redis.Pool
}

type TaskResponse struct {
	slot  string
	raw   []byte
	conn  *redis.Pool
	ready bool
}

var pools = make(map[string]*redis.Pool)

func NewTasks(taskName, address string) *Task {
	if address == "" {
		address = "redis:6379"
	}
	conn := pools[address]
	if conn == nil {
		conn = redis.NewPool(func() (redis.Conn, error) {
			return redis.Dial("tcp", address)
		}, 2)
		pools[address] = conn
	}

	return &Task{
		conn:           conn,
		taskName:       taskName,
		payloadChannel: taskName + ".input_channel",
		outputSlot:     taskName + ".output_slot",
		waitSlot:       taskName + ".wait_slot",
	}
}

func (t *Task) IssueRaw(input []byte) *TaskResponse {
	conn := t.conn.Get()
	result_slot := t.taskName + ".output_slot." + randStringGen(20)
	levlog.E(conn.Send("MULTI"))
	levlog.E(conn.Send("LPUSH", t.payloadChannel, input))
	levlog.E(conn.Send("LPUSH", t.outputSlot, result_slot))
	levlog.E(conn.Send("LPUSH", t.waitSlot, 0))
	result, err := conn.Do("EXEC")
	levlog.E(err)
	if err != nil {
		return nil
	}
	levlog.Debug(result, err)
	return &TaskResponse{
		slot:  result_slot,
		conn:  t.conn,
		ready: false,
	}
}

func (t *Task) IssueJson(jsonbObj interface{}) *TaskResponse {
	payload, err := json.Marshal(jsonbObj)
	levlog.E(err)
	if err != nil {
		return nil
	}
	return t.IssueRaw(payload)
}

func (t *TaskResponse) Ready() bool {
	if t.ready {
		return true
	}
	result, err := redis.Int(t.conn.Get().Do("LLEN", t.slot))
	levlog.E(err)
	return result == 1
}

func (t *TaskResponse) GetRaw() []byte {
	if t.raw == nil {
		levlog.Debug("getting result from KEY:", t.slot)
		result, err := redis.Strings(t.conn.Get().Do("BRPOP", t.slot, 60))
		if err != nil {
			levlog.E(err)
			return nil
		}
		t.raw = []byte(result[1])
		t.ready = true
	}
	return t.raw
}

func (t *TaskResponse) GetJson() *JsonType {
	raw := t.GetRaw()
	if raw == nil {
		levlog.Error("get empty response")
	}
	return NewJsonFromString(string(raw))
}
